﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;
using HeapSpecSupport;
using SimpleIR;

namespace Evaluator.EvalState
{
	internal sealed class LocalFrame
	{
		//////////
		//Info for tracking the local evaluation state.

		/// <summary>
		/// States the debugger can be in.
		/// </summary>
		internal enum DebugState { DebugRun, DebugBreakAt, DebugStep, DebugStepInto };

		/// <summary>
		/// The current state of the debugger
		/// </summary>
		private DebugState m_debug;

		/// <summary>
		/// The method that this frame is assocated with.
		/// </summary>
		private readonly IRBodyDef m_method;

		/// <summary>
		/// Is this an explicit cons invoke that implicitly returns "this"
		/// </summary>
		private readonly bool m_isconsinvoke;

		/// <summary>
		/// The basic block that is currently being executed.
		/// </summary>
		private IRBasicBlock m_currentblock;

		/// <summary>
		/// The opcode within the basic block that is being executed.
		/// </summary>
		private int m_progcounter;

		//////////
		//Representation of the local program state.

		/// <summary>
		/// The eval stack.
		/// </summary>
		private readonly List<EvalStackEntry> m_evalstack;

		/// <summary>
		/// Map from local var names to the locations that hold their values.
		/// </summary>
		private readonly Dictionary<IRVarName, int> m_localsmap;

		[ContractInvariantMethod]
		private void ClassInvariant()
		{
			Contract.Invariant(this.m_method != null);
			Contract.Invariant(this.m_currentblock != null);

			Contract.Invariant(this.m_evalstack != null);
			Contract.Invariant(this.m_localsmap != null);
		}

		internal LocalFrame(IRBodyDef bb, bool isconsinvoke, DebugState dbgs)
		{
			this.m_debug = dbgs;

			this.m_method = bb;
			this.m_isconsinvoke = isconsinvoke;
			this.m_currentblock = bb.Body.GetMethodEntryBlock();
			this.m_progcounter = 0;

			this.m_evalstack = new List<EvalStackEntry>();
			this.m_localsmap = new Dictionary<IRVarName, int>();
		}

		/// <summary>
		/// Pop (as needed) the return from this call frame onto the caller stack.
		/// </summary>
		internal void PopReturnValueIntoFramePropDebug(LocalFrame caller)
		{
			Contract.Requires(caller != null);

			if(this.m_isconsinvoke || this.m_method.ReturnType != ExeProgram.TProg.TypeTable.VoidType)
				caller.EvalStackPush(this.EvalStackPopWVal());
		}

		/// <summary>
		/// Get and set the state of the debugger in the current frame.
		/// </summary>
		internal DebugState DebuggerState
		{
			get { return this.m_debug; }
			set { this.m_debug = value; }
		}

		/// <summary>
		/// Update the current execution point to be the first instruction in the block given by the jump target.
		/// </summary>
		internal void JumpToNewBlock(long jtrgt)
		{
			this.m_currentblock = this.m_method.Body.GetBlockWithID(jtrgt);
			this.m_progcounter = 0;
		}

		/// <summary>
		/// Increment the program counter to the next instruction in the block.
		/// </summary>
		internal void IncrementPC()
		{
			Contract.Assert(this.m_progcounter < this.m_currentblock.Operations.Count);

			this.m_progcounter++;
		}

		/// <summary>
		/// Get the method that this local frame is executing.
		/// </summary>
		internal IRBodyDef CurrentMethod
		{ get { return this.m_method; } }

		/// <summary>
		/// Get the bytecode operation at the current program counter point.
		/// </summary>
		internal IRByteCode CurrentOperation
		{ get { return this.m_currentblock.Operations[this.m_progcounter]; } }

		/// <summary>
		/// Clear dead and no longer used variables as needed, and add newly inscope vars.
		/// Note this may leave some dangling nodes that previously contained the contents of the variables so until you normalize
		/// this may be a bit odd.
		/// </summary>
		/// <param name="liveAfter">Sorted List of vars in scope after this stmt.</param>
		internal void ClearVarsNotInScopeAddNew(HashSet<IRVarName> inscope, Dictionary<IRVarName, IRType> nametotypemap, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(inscope != null);
			Contract.Requires(nametotypemap != null);

			List<IRVarName> toelim = new List<IRVarName>();
			foreach(IRVarName rrp in this.m_localsmap.Keys)
			{
				if(!inscope.Contains(rrp))
					toelim.Add(rrp);
			}

			for(int i = 0; i < toelim.Count; ++i)
				this.m_localsmap.Remove(toelim[i]);

			foreach(IRVarName rri in inscope)
			{
				if(!this.m_localsmap.ContainsKey(rri))
				{
					IRType lvt = nametotypemap[rri];

					int caddr = memory.Count;
					EHomeLocation loc;
					if(lvt is IRNumericType)
						loc = new EHomeLocation(lvt, caddr, false, ENumeric.CreateDefaultValueForType((IRNumericType)lvt), ctime, line);
					else if(lvt is IREnumType)
						loc = new EHomeLocation(lvt, caddr, false, ENumeric.CreateDefaultValueForType(((IREnumType)lvt).UnerlyingNumericType), ctime, line);
					else if(lvt is IRPtrType)
						loc = new EHomeLocation(lvt, caddr, false, EPointer.CreateDefaultNullPointer((IRPtrType)lvt), ctime, line);
					else if(lvt is IRReferenceType)
						loc = new EHomeLocation(lvt, caddr, false, EReference.CreateDefaultNullReference((IRReferenceType)lvt), ctime, line);
					else
						loc = new EHomeLocation(lvt, caddr, false, new BAPLayoutMap(lvt.TypeDescription.AllTerminalAccessPaths, ctime, line), ctime, line);

					this.m_localsmap.Add(rri, loc.Address);
					memory.Add(loc);
				}
			}
		}

		/// <summary>
		/// Add a fixed allocation site for a static name.
		/// </summary>
		internal static void AddGlobalScopeVar(IRStaticField sf, Dictionary<IRStaticField, int> globalmap, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(sf != null);
			int caddr = memory.Count;
			
			EHomeLocation loc;
			if(sf.FieldType is IRNumericType)
				loc = new EHomeLocation(sf.FieldType, caddr, true, ENumeric.CreateDefaultValueForType((IRNumericType)sf.FieldType), ctime, line);
			else if(sf.FieldType is IREnumType)
				loc = new EHomeLocation(sf.FieldType, caddr, true, ENumeric.CreateDefaultValueForType(((IREnumType)sf.FieldType).UnerlyingNumericType), ctime, line);
			else if(sf.FieldType is IRPtrType)
				loc = new EHomeLocation(sf.FieldType, caddr, true, EPointer.CreateDefaultNullPointer((IRPtrType)sf.FieldType), ctime, line);
			else
				loc = new EHomeLocation(sf.FieldType, caddr, true, new BAPLayoutMap(sf.FieldType.TypeDescription.AllTerminalAccessPaths, ctime, line), ctime, line);

			globalmap.Add(sf, loc.Address);
			memory.Add(loc);
		}

		/// <summary>
		/// Peek at the top value from the eval stack.
		/// </summary>
		internal EvalStackEntry EvalStackPeek()
		{
			if(this.m_evalstack.Count == 0)
				DebugReport.ReportError("Peek on empty eval stack.");

			return this.m_evalstack[this.m_evalstack.Count - 1];
		}

		/// <summary>
		/// Peek at the top value from the eval stack.
		/// </summary>
		internal EvalStackEntry EvalStackPeekK(int k)
		{
			if(this.m_evalstack.Count <= k)
				DebugReport.ReportError("Peek past bottom of stack.");

			return this.m_evalstack[this.m_evalstack.Count - (k + 1)];
		}

		/// <summary>
		/// Remove the top value from the eval stack.
		/// </summary>
		internal EvalStackEntry EvalStackPopWVal()
		{
			if(this.m_evalstack.Count == 0)
				DebugReport.ReportError("Pop on empty eval stack.");

			EvalStackEntry val = this.m_evalstack[this.m_evalstack.Count - 1];
			this.m_evalstack.RemoveAt(this.m_evalstack.Count - 1);
			return val;
		}

		/// <summary>
		/// Remove the top value from the eval stack.
		/// </summary>
		internal void EvalStackPop()
		{
			if(this.m_evalstack.Count == 0)
				DebugReport.ReportError("Pop on empty eval stack.");

			this.m_evalstack.RemoveAt(this.m_evalstack.Count - 1);
		}

		/// <summary>
		/// Push to this node on the top of the eval stack.
		/// </summary>
		internal void EvalStackPush(EvalStackEntry val)
		{ this.m_evalstack.Add(val.CopyStackEntry()); }

		/// <summary>
		/// Push to this node on the top of the eval stack.
		/// </summary>
		internal void EvalStackPush(EValue val)
		{
			IRType evpt = val.TypeOfValue;
			EValue pval = val;

			if(evpt is IRNumericType)
			{
				if(evpt is IRSignedIntegerType)
					pval = new ENumeric(ExeProgram.TProg.TypeTable.Int64Type, ((ENumeric)val).SignedNumericValue);
				else if(evpt is IRUnSignedIntegerType)
					pval = new ENumeric(ExeProgram.TProg.TypeTable.UInt64Type, ((ENumeric)val).UnSignedNumericValue);
				else
					pval = new ENumeric(ExeProgram.TProg.TypeTable.Float64Type, ((ENumeric)val).FloatNumericValue);
			}
			
			this.m_evalstack.Add(EvalStackEntry.MakeValueStackEntry(pval));
		}

		/// <summary>
		/// Push to this node on the top of the eval stack.
		/// </summary>
		internal void EvalStackPush(IRFunctionPointerType fptype, IRInvokeIdentity val)
		{ this.m_evalstack.Add(EvalStackEntry.MakeFunctionPtrStackEntry(fptype, val)); }

		/// <summary>
		/// Push to this node on the top of the eval stack.
		/// </summary>
		internal void EvalStackPush(IRStructType structtype, BAPLayoutMap val)
		{ this.m_evalstack.Add(EvalStackEntry.MakeStructStackEntry(structtype, val)); }

		////////////////////

		/// <summary>
		/// Resolve the location for a global variable.
		/// </summary>
		internal static EHomeLocation ResolveGlobalVarTarget(IRStaticField sf, Dictionary<IRStaticField, int> globalmap, List<ELocation> memory)
		{
			Contract.Requires(sf != null);
			Contract.Requires(globalmap != null);
			Contract.Requires(memory != null);
			Contract.Ensures(Contract.Result<EHomeLocation>() != null);
			Contract.Ensures(Contract.Result<EHomeLocation>().IsGlobalLocation);

			return (EHomeLocation)memory[globalmap[sf]]; 
		}

		/// <summary>
		/// Resolve the location for a local variable.
		/// </summary>
		internal static EHomeLocation ResolveLocalVarTarget(IRVarName localvar, Dictionary<IRVarName, int> localmap, List<ELocation> memory)
		{
			Contract.Requires(localvar != null);
			Contract.Requires(localmap != null);
			Contract.Requires(memory != null);
			Contract.Ensures(Contract.Result<EHomeLocation>() != null);
			Contract.Ensures(!Contract.Result<EHomeLocation>().IsGlobalLocation);

			return (EHomeLocation)memory[localmap[localvar]]; 
		}

		/// <summary>
		/// Resolve the location that the pointer is to.
		/// </summary>
		internal static EObject ResolvePointerTarget(EPointer p, List<ELocation> memory)
		{
			Contract.Requires(p != null);
			Contract.Requires(memory != null);
			Contract.Ensures(Contract.Result<EObject>() != null);
			
			return (EObject)memory[p.TargetObject]; 
		}

		/// <summary>
		/// Resolve the location that the reference is to.
		/// </summary>
		internal static ELocation ResolveReferenceTarget(EReference r, List<ELocation> memory)
		{
			Contract.Requires(r != null);
			Contract.Requires(memory != null);
			Contract.Ensures(Contract.Result<ELocation>() != null);

			return memory[r.TargetLocation]; 
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//Implementation of the abstract operations
		
		//////////////
		//Allocation

		/// <summary>
		/// Simulate the allocation of an object or struct (YOU MUST DO initialization call later if desired).
		/// Note we don't let you alloc raw pointers or blank arrays (because they always need a size parameter).
		/// </summary>
		internal void SimulateAllocBlank(IRType alloctype, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(alloctype != null);
			Contract.Requires((!(alloctype is IRContainerType)) || (!((IRContainerType)alloctype).IsArrayContainer));

			if(alloctype is IRStructType)
			{
				BAPLayoutMap sv = new BAPLayoutMap(alloctype.TypeDescription.AllTerminalAccessPaths, ctime, line);
				this.EvalStackPush((IRStructType)alloctype, sv);
			}
			else
			{
				IRPtrType drftype = ExeProgram.TProg.TypeTable.LookupPointerTypeFor((IRClassType)alloctype);
				EObject nloc = EObject.AllocateBasicObjectOfType(memory, (IRClassType)alloctype, ctime, line);
				
				EPointer pv = new EPointer(drftype, nloc.Address);
				this.EvalStackPush(pv);
			}
		}

		//////////////
		//Primitive Load/Store

		/// <summary>
		/// Simulate the load of a value from a local variable to the stack.
		/// </summary>
		internal void SimulateLoadFromLocal(IRVarName rv, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(rv != null);

			EHomeLocation lvn = ResolveLocalVarTarget(rv, this.m_localsmap, memory);
			if(lvn.LocationType is IRStructType)
			{
				BAPLayoutMap svals = new BAPLayoutMap(((IRStructType)lvn.LocationType).TypeDescription.AllTerminalAccessPaths, ctime, line);
				lvn.DoCompoundReadFromLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, svals, ctime, line);
				this.EvalStackPush((IRStructType)lvn.LocationType, svals);
			}
			else
			{
				EValue val = lvn.DoSimpleValueReadFromLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, ctime, line);
				this.EvalStackPush(val);
			}
		}

		/// <summary>
		/// Simulate the store of a value to a local variable from the stack.
		/// </summary>
		internal void SimulateStoreToLocal(IRVarName rv, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(rv != null);

			EvalStackEntry evse = this.EvalStackPopWVal();
			EHomeLocation lvn = ResolveLocalVarTarget(rv, this.m_localsmap, memory);
			if(lvn.LocationType is IRStructType)
				lvn.DoCompoundWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, evse.StructValue, ctime, line);
			else
			{
				if(lvn.LocationType is IRNumericType)
				{
					ENumeric sval = evse.NumericValue.ConvertTo((IRNumericType)lvn.LocationType);
					lvn.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, sval, ctime, line);
				}
				else if(lvn.LocationType is IREnumType)
				{
					ENumeric sval = evse.NumericValue.ConvertTo(((IREnumType)lvn.LocationType).UnerlyingNumericType);
					lvn.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, sval, ctime, line);
				}
				else if(lvn.LocationType is IRPtrType)
				{
					EPointer pval = evse.PointerValue;
					Contract.Assert(IRProgram.IsT1AssignableToT2(pval.PointerType, lvn.LocationType));
					EPointer wpval = pval.ConvertPointerType((IRPtrType)lvn.LocationType);
					lvn.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, wpval, ctime, line);
				}
				else if(lvn.LocationType is IRReferenceType)
				{
					EReference rval = evse.ReferenceValue;
					Contract.Assert(rval.ReferenceType == lvn.LocationType);
					lvn.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, rval, ctime, line);
				}
				else
				{
					DebugReport.ReportError("Unknown type to store??");
				}
			}
		}

		/// <summary>
		/// Simulate the load of a value from a global variable to the stack.
		/// </summary>
		internal void SimulateLoadFromGlobal(IRStaticField sf, Dictionary<IRStaticField, int> globalmap, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(sf != null);

			EHomeLocation gln = ResolveGlobalVarTarget(sf, globalmap, memory);
			if(gln.LocationType is IRStructType)
			{
				BAPLayoutMap svals = new BAPLayoutMap(((IRStructType)gln.LocationType).TypeDescription.AllTerminalAccessPaths, ctime, line);
				gln.DoCompoundReadFromLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, svals, ctime, line);
				this.EvalStackPush((IRStructType)gln.LocationType, svals);
			}
			else
			{
				EValue val = gln.DoSimpleValueReadFromLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, ctime, line);
				this.EvalStackPush(val);
			}
		}

		/// <summary>
		/// Simulate the store of a value to a global variable from the stack.
		/// </summary>
		internal void SimulateStoreToGlobal(IRStaticField sf, Dictionary<IRStaticField, int> globalmap, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(sf != null);

			EvalStackEntry evse = this.EvalStackPopWVal();
			EHomeLocation gln = ResolveGlobalVarTarget(sf, globalmap, memory);
			if(gln.LocationType is IRStructType)
				gln.DoCompoundWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, evse.StructValue, ctime, line);
			else
			{
				if(gln.LocationType is IRNumericType)
				{
					ENumeric sval = evse.NumericValue.ConvertTo((IRNumericType)gln.LocationType);
					gln.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, sval, ctime, line);
				}
				else if(gln.LocationType is IREnumType)
				{
					ENumeric sval = evse.NumericValue.ConvertTo(((IREnumType)gln.LocationType).UnerlyingNumericType);
					gln.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, sval, ctime, line);
				}
				else if(gln.LocationType is IRPtrType)
				{
					EPointer pval = evse.PointerValue;
					Contract.Assert(IRProgram.IsT1AssignableToT2(pval.PointerType, gln.LocationType));
					EPointer wpval = pval.ConvertPointerType((IRPtrType)gln.LocationType);
					gln.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, wpval, ctime, line);
				}
				else if(gln.LocationType is IRReferenceType)
				{
					EReference rval = evse.ReferenceValue;
					Contract.Assert(rval.ReferenceType == gln.LocationType);
					gln.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath, rval, ctime, line);
				}
				else
				{
					DebugReport.ReportError("Unknown type to store??");
				}
			}
		}

		//////////////
		//Primitive Compare

		/// <summary>
		/// Simulate the equality operation.
		/// </summary>
		internal void SimulatePrimEQ()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Eq(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate the less operation.
		/// </summary>
		internal void SimulateLess()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Lt(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate the less or equal operation.
		/// </summary>
		internal void SimulateLessEQ()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.LtEq(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate the greater operation.
		/// </summary>
		internal void SimulateGreater()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Gt(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate the greater or equal operation.
		/// </summary>
		internal void SimulateGreaterEQ()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.GtEq(pn2);
			this.EvalStackPush(res);
		}

		//////////////
		//Primitive Arith Operations
		//and
		//Primitive Bitwise Operations

		/// <summary>
		/// Simulate an arbitrary add operation.
		/// </summary>
		internal void SimulateAdd()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Add(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary div operation.
		/// </summary>
		internal void SimulateDiv()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Div(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary mult operation.
		/// </summary>
		internal void SimulateMult()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Mult(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary neg operation.
		/// </summary>
		internal void SimulateNeg()
		{
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Neg();
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary rem operation.
		/// </summary>
		internal void SimulateRem()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Rem(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary sub operation.
		/// </summary>
		internal void SimulateSub()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Sub(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary and operation.
		/// </summary>
		internal void SimulateAnd()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.And(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary or operation.
		/// </summary>
		internal void SimulateOr()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Or(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary not operation.
		/// </summary>
		internal void SimulateNot()
		{
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Not();
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary shl operation.
		/// </summary>
		internal void SimulateShl()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Shl(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary shr operation.
		/// </summary>
		internal void SimulateShr()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Shr(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Simulate an arbitrary xor operation.
		/// </summary>
		internal void SimulateXor()
		{
			ENumeric pn2 = this.EvalStackPopWVal().NumericValue;
			ENumeric pn1 = this.EvalStackPopWVal().NumericValue;

			ENumeric res = pn1.Xor(pn2);
			this.EvalStackPush(res);
		}

		/// <summary>
		/// Analyze a conversion of one primitive type to another (soffset is the offset from the stack top -- 0).
		/// </summary>
		internal void SimulateConvOp(IRNumericType cto, int soffset)
		{
			Contract.Requires(cto != null);

			int spos = this.m_evalstack.Count - (soffset + 1);
			
			ENumeric pn1 = this.m_evalstack[spos].NumericValue;
			ENumeric res = pn1.ConvertTo(cto);
			ENumeric ressv = res.ConvertTo((IRNumericType)ExeProgram.TProg.ConvertTypeToStdStackRep(cto));
			this.m_evalstack[spos] = EvalStackEntry.MakeValueStackEntry(ressv);
		}

		//////////////
		//Load/Store Through References

		/// <summary>
		/// Read a value through the Reference.
		/// </summary>
		internal void SimulateDeRefIndirectRead(List<ELocation> memory, ulong ctime, long line)
		{
			EReference vref = this.EvalStackPopWVal().ReferenceValue;
			ENumeric vrefnull = vref.ReferenceNullTest();
			if(vrefnull.IsValueOne)
			{
				DebugReport.ReportError("Load on null reference.");
				throw new InvalidOperationException();
			}

			ELocation rloc = ResolveReferenceTarget(vref, memory);
			if(rloc.LocationType is IRStructType)
			{
				BAPLayoutMap into = new BAPLayoutMap(((IRStructType)rloc.LocationType).TypeDescription.AllTerminalAccessPaths, ctime, line);
				rloc.DoCompoundReadFromLocation(vref.InitialArrayIndex, vref.Path, into, ctime, line);
				this.EvalStackPush((IRStructType)vref.ReferenceType.DerefTargetType, into);
			}
			else
			{
				EValue lv = rloc.DoSimpleValueReadFromLocation(vref.InitialArrayIndex, vref.Path, ctime, line);
				this.EvalStackPush(lv);
			}
		}

		/// <summary>
		/// Store a value through the reference.
		/// </summary>
		internal void SimulateDeRefIndirectWrite(List<ELocation> memory, ulong ctime, long line)
		{
			EvalStackEntry vsv = this.EvalStackPopWVal();
			EReference vref = this.EvalStackPopWVal().ReferenceValue;
			ENumeric vrefnull = vref.ReferenceNullTest();
			if(vrefnull.IsValueOne)
			{
				DebugReport.ReportError("Store on null reference.");
				throw new InvalidOperationException();
			}

			IRType storetype = vref.ReferenceType.DerefTargetType;
			ELocation storeloc = ResolveReferenceTarget(vref, memory);

			if(storetype is IRStructType)
				storeloc.DoCompoundWriteToLocation(vref.InitialArrayIndex, vref.Path, vsv.StructValue, ctime, line);
			else
			{
				if(storetype is IRNumericType)
				{
					ENumeric sval = vsv.NumericValue.ConvertTo((IRNumericType)storetype);
					storeloc.DoSimpleValueWriteToLocation(vref.InitialArrayIndex, vref.Path, sval, ctime, line);
				}
				else if(storetype is IREnumType)
				{
					ENumeric sval = vsv.NumericValue.ConvertTo(((IREnumType)storetype).UnerlyingNumericType);
					storeloc.DoSimpleValueWriteToLocation(vref.InitialArrayIndex, vref.Path, sval, ctime, line);
				}
				else if(storetype is IRPtrType)
				{
					EPointer pval = vsv.PointerValue;
					Contract.Assert(IRProgram.IsT1AssignableToT2(pval.PointerType, storetype));
					EPointer wpval = pval.ConvertPointerType((IRPtrType)storetype);
					storeloc.DoSimpleValueWriteToLocation(vref.InitialArrayIndex, vref.Path, wpval, ctime, line);
				}
				else if(storetype is IRReferenceType)
				{
					EReference rval = vsv.ReferenceValue;
					Contract.Assert(rval.ReferenceType == storetype);
					storeloc.DoSimpleValueWriteToLocation(vref.InitialArrayIndex, vref.Path, rval, ctime, line);
				}
				else
				{
					DebugReport.ReportError("Unknown type to store??");
				}
			}
		}
		
		/// <summary>
		/// Initialize a struct/class value through the reference.
		/// </summary>
		internal void SimulateDeRefIndirectInitializeOfStruct(List<ELocation> memory, IRType toinit, ulong ctime, long line)
		{
			EReference vref = this.EvalStackPopWVal().ReferenceValue;
			BAPLayoutMap vsv = new BAPLayoutMap(toinit.TypeDescription.AllTerminalAccessPaths, ctime, line);

			ELocation rloc = ResolveReferenceTarget(vref, memory);
			rloc.DoCompoundWriteToLocation(vref.InitialArrayIndex, vref.Path, vsv, ctime, line);
		}

		/// <summary>
		/// Do a load from a struct on the evaluation stack.
		/// </summary>
		internal void SimulateReadFromStructOnStack(IRMemberField mf, ulong ctime, long line)
		{
			BAPLayoutMap sstr = this.EvalStackPopWVal().StructValue;

			IRType trgttype = mf.FieldType;
			IRBasicAccessPath mfp = ExeProgram.TProg.GetAccessPathForSingleField(mf);
			if(trgttype is IRStructType)
			{
				BAPLayoutMap into = new BAPLayoutMap(trgttype.TypeDescription.AllTerminalAccessPaths, ctime, line);
				sstr.ReadFieldValues(BAPLayoutEntry.INVALID_ARRAY_INDEX, mfp, into, ctime, line);
				this.EvalStackPush((IRStructType)trgttype, into);
			}
			else
			{
				EValue mval = sstr.ReadFieldValue(BAPLayoutEntry.INVALID_ARRAY_INDEX, mfp, ctime, line);
				this.EvalStackPush(mval);
			}
		}

		//////////////
		//Pointer Creation and Indexing

		/// <summary>
		/// Load the address of a local variable.
		/// </summary>
		internal void SimulateLoadLocalAddr(IRVarName rv, List<ELocation> memory)
		{
			Contract.Requires(rv != null);

			EHomeLocation lhv = ResolveLocalVarTarget(rv, this.m_localsmap, memory);
			IRReferenceType reftype = ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(lhv.LocationType);
			EReference erv = new EReference(reftype, lhv.Address, -1, IRBasicAccessPath.EmptyAccessPath);
			this.EvalStackPush(erv);
		}

		/// <summary>
		/// Load the address of a global variable.
		/// </summary>
		internal void SimulateLoadLocalAddr(IRStaticField sf, Dictionary<IRStaticField, int> globals, List<ELocation> memory)
		{
			Contract.Requires(sf != null);

			EHomeLocation lhv = ResolveGlobalVarTarget(sf, globals, memory);
			IRReferenceType reftype = ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(lhv.LocationType);
			EReference erv = new EReference(reftype, lhv.Address, -1, IRBasicAccessPath.EmptyAccessPath);
			this.EvalStackPush(erv);
		}

		/// <summary>
		/// Index a pointer by the given member field, also take the position on the stack of the pointer (0 is the top element).
		/// </summary>
		internal void SimulateIndexAddrByFieldInclContainer(IRMemberField mf, int containerindex, int sloc)
		{
			Contract.Requires(mf != null);
			Contract.Assert((0 <= sloc) & (sloc < this.m_evalstack.Count));

			EvalStackEntry evse = this.m_evalstack[this.m_evalstack.Count - (sloc + 1)];
			EReference ptrn;
			if(evse.SLocType is IRReferenceType)
				ptrn = evse.ReferenceValue;
			else
			{
				IRReferenceType crt = ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(evse.PointerValue.PointerType.DerefTargetType);
				ptrn = new EReference(crt, evse.PointerValue.TargetObject, -1, IRBasicAccessPath.EmptyAccessPath);
			}

			Contract.Assert(ptrn.ReferenceType.DerefTargetType.TypeDescription.AllVisibleFields.Contains(mf));

			EReference idxr;
			if(mf.IsSummaryField)
				idxr = ptrn.IndexAddrByIndex(containerindex, mf);
			else
				idxr = ptrn.IndexAddrByField(mf);

			this.m_evalstack[this.m_evalstack.Count - (sloc + 1)] = EvalStackEntry.MakeValueStackEntry(idxr);
		}
			
		/// <summary>
		/// Simulate the load of a known function pointer to the stack.
		/// </summary>
		internal void SimulateLoadFuncPtr(IRInvokeIdentity invid)
		{
			EvalStackEntry fpv = EvalStackEntry.MakeFunctionPtrStackEntry(ExeProgram.TProg.TypeTable.FunctionPtrType, invid);
			this.EvalStackPush(fpv);
		}

		//////////////
		//Pointer Compare
		
		/// <summary>
		/// Equality of the references or pointers on top of the stack.
		/// </summary>
		internal void SimulateDrefableEQ()
		{
			if(this.EvalStackPeek().SLocType is IRPtrType)
			{
				EPointer p1 = this.EvalStackPopWVal().PointerValue;
				EPointer p2 = this.EvalStackPopWVal().PointerValue;

				ENumeric rv = EPointer.PointerEqualsTest(p1, p2);
				this.EvalStackPush(rv);
			}
			else
			{
				EReference r1 = this.EvalStackPopWVal().ReferenceValue;
				EReference r2 = this.EvalStackPopWVal().ReferenceValue;

				ENumeric rv = EReference.ReferenceEqualsTest(r1, r2);
				this.EvalStackPush(rv);
			}
		}

		/// <summary>
		/// Is the pointer on top of the stack non-null.
		/// </summary>
		internal void SimulatePointerNonNull()
		{
			if(this.EvalStackPeek().SLocType is IRPtrType)
			{
				EPointer p = this.EvalStackPopWVal().PointerValue;
				ENumeric rv = p.PointerNullTest();
				this.EvalStackPush(rv);
			}
			else
			{
				EReference r = this.EvalStackPopWVal().ReferenceValue;
				ENumeric rv = r.ReferenceNullTest();
				this.EvalStackPush(rv);
			}
		}

		//////////////
		//Type Operations
		
		/// <summary>
		/// Box the valuetype on the top of the stack.
		/// </summary>
		internal void SimulateBox(IRValueType typetobox, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(typetobox != null);

			IRBoxedType boxedty = ExeProgram.TProg.TypeTable.LookupBoxedTypeFor(typetobox);
			IRPtrType ptrtoboxedty = ExeProgram.TProg.TypeTable.LookupPointerTypeFor(boxedty);
			IRReferenceType reftoboxedty = ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(boxedty);

			EObject bxn = EObject.AllocateBasicObjectOfType(memory, boxedty, ctime, line);
			IRMemberField boxf = boxedty.BoxedValueField;
			IRBasicAccessPath boxbap = ExeProgram.TProg.GetAccessPathForSingleField(boxf);
			EReference refv = new EReference(ExeProgram.TProg.TypeTable.LookupReferenceTypeFor(typetobox), bxn.Address, BAPLayoutEntry.INVALID_ARRAY_INDEX, boxbap);
			EPointer nsn = new EPointer(ptrtoboxedty, bxn.Address);

			this.EvalStackPush(nsn);
			this.EvalStackPush(refv);
			this.SimulateDeRefIndirectWrite(memory, ctime, line);
		}

		/// <summary>
		/// Unbox the boxed object on top of the stack (e.g. get a pointer to the boxed contents).
		/// </summary>
		internal void SimulateUnBox(IRValueType typetounbox, int sloc)
		{
			Contract.Requires(typetounbox != null);

			int ssloc = (sloc == -1 ? (this.m_evalstack.Count - 1) : sloc);
			EReference sn = this.m_evalstack[ssloc].ReferenceValue;

			ENumeric isnull = sn.ReferenceNullTest();
			if(isnull.IsValueOne)
			{
				DebugReport.ReportError("Unbox of null reference, we should fix this for nullable types at some point in time.");
				throw new InvalidOperationException();
			}

			IRBoxedType boxetype = ExeProgram.TProg.TypeTable.LookupBoxedTypeFor(typetounbox);
			IRMemberField boxfield = boxetype.BoxedValueField;

			EReference tocontents = sn.IndexAddrByField(boxfield);
			this.m_evalstack[ssloc] = EvalStackEntry.MakeValueStackEntry(tocontents);
		}

		/// <summary>
		/// Cast with throw to given type.
		/// </summary>
		internal void SimulateCast(IRPtrType castto, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(castto != null);

			bool isinst = this.CheckIsInstOfStackTop(castto, true, memory, ctime, line);
			if(!isinst)
			{
				DebugReport.ReportError("Invalid cast");
				throw new InvalidOperationException();
			}
		}

		/// <summary>
		/// Cast without throw to given type (null is pushed if not possible otherwise the recast pointer is).
		/// </summary>
		internal void SimulateIsInst(IRPtrType instof, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(instof != null);

			this.CheckIsInstOfStackTop(instof, false, memory, ctime, line);
		}

		/// <summary>
		/// return true if the top value on the stack is a subtype of the given derefable (treats a null pointer as a subtype if 
		/// <c>nulloksubtype</c> is true otherwise null is not a subtype of any type).
		/// </summary>
		private bool CheckIsInstOfStackTop(IRPtrType instof, bool nulloksubtype, List<ELocation> memory, ulong ctime, long line)
		{
			EPointer pn = this.EvalStackPopWVal().PointerValue;
			ENumeric nv = pn.PointerNullTest();

			if(nv.IsValueOne)
			{
				this.EvalStackPush(EPointer.CreateDefaultNullPointer(instof));
				return nulloksubtype;
			}

			EObject ploc = ResolvePointerTarget(pn, memory);
			IRMemberField smf = ExeProgram.TProg.TypeTable.RootObjectRTTIField;
			IRBasicAccessPath rttip = ExeProgram.TProg.GetAccessPathForSingleField(smf);
			ploc.DoSimpleValueReadFromLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, rttip, ctime, line);

			bool ok = instof.DerefTargetType.TypeDescription.AllSubTypes.Contains(ploc.ObjectType);
			if(!ok)
				this.EvalStackPush(EPointer.CreateDefaultNullPointer(instof));
			else
			{
				EPointer npv = pn.ConvertPointerType(instof);
				this.EvalStackPush(npv);
			}
				

			return ok;
		}

		/// <summary>
		/// Convert a pointer to a reference.
		/// </summary>
		internal void SimulateConvToReference(int argoffset, IRReferenceType reftype)
		{
			Contract.Requires(reftype != null);

			int spos = this.m_evalstack.Count - (argoffset + 1);
			EvalStackEntry sn = this.m_evalstack[spos];
			Contract.Assert(sn.SLocType is IRPtrType);

			//change the type of the SLoc
			EReference rr = new EReference(reftype, sn.PointerValue.TargetObject, BAPLayoutEntry.INVALID_ARRAY_INDEX, IRBasicAccessPath.EmptyAccessPath);
			this.m_evalstack[spos] = EvalStackEntry.MakeValueStackEntry(rr);
		}

		/// <summary>
		/// Simulate the constrain operation for method calls.
		/// </summary>
		internal void SimulateConstrain(int argoffset, IRReferenceType desiredtype, List<ELocation> memory, ulong ctime, long line)
		{
			Contract.Requires(desiredtype != null);

			int spos = this.m_evalstack.Count - (argoffset + 1);
			EvalStackEntry sn = this.m_evalstack[spos];
			Contract.Assert(sn.SLocType is IRReferenceType);
			this.EvalStackPush(sn);

			if(desiredtype.DerefTargetType is IRValueType)
			{
				this.SimulateDeRefIndirectRead(memory, ctime, line);
				this.SimulateBox((IRValueType)desiredtype.DerefTargetType, memory, ctime, line);
			}
			else
			{
				this.SimulateDeRefIndirectRead(memory, ctime, line);
			}

			EvalStackEntry resn = this.EvalStackPopWVal();
			this.m_evalstack[spos] = resn;
		}

		//////////////
		//Misc Operations
		
		/// <summary>
		/// Pop the top of the stack.
		/// </summary>
		internal void SimulatePop()
		{
			this.EvalStackPop();
		}

		/// <summary>
		/// Duplicate the element on the top of the stack.
		/// </summary>
		internal void SimulateDup()
		{
			EvalStackEntry sn = this.EvalStackPeek();
			this.EvalStackPush(sn);
		}

		/// <summary>
		///Push the null literal on the stack.
		/// </summary>
		internal void SimulatePushNull()
		{
			EPointer np = EPointer.CreateDefaultNullPointer(ExeProgram.TProg.TypeTable.NullType);
			this.EvalStackPush(np);
		}

		/// <summary>
		/// Push the given int literal on the stack.
		/// </summary>
		internal void SimulatePushInt(long val)
		{
			ENumeric esi = new ENumeric(ExeProgram.TProg.TypeTable.Int64Type, val);
			this.EvalStackPush(esi);
		}

		/// <summary>
		/// Push the given int literal on the stack.
		/// </summary>
		internal void SimulatePushUInt(ulong val)
		{
			ENumeric ei = new ENumeric(ExeProgram.TProg.TypeTable.UInt64Type, val);
			this.EvalStackPush(ei);
		}

		/// <summary>
		/// Push the given float literal on the stack.
		/// </summary>
		internal void SimulatePushFloat(double fval)
		{
			ENumeric efl = new ENumeric(ExeProgram.TProg.TypeTable.Float64Type, fval);
			this.EvalStackPush(efl);
		}

		/// <summary>
		/// Push the given string literal on the stack.
		/// </summary>
		internal void SimulatePushString(List<ELocation> memory, ulong ctime, long line, string strval)
		{
			EStringObject sloc = (EStringObject)EObject.AllocateBasicObjectOfType(memory, ExeProgram.TProg.TypeTable.StringObjectType, ctime, line);
			sloc.SimulateStringConstructor(strval.ToCharArray(), ctime, line);

			IRPtrType sptr = ExeProgram.TProg.TypeTable.LookupPointerTypeFor(ExeProgram.TProg.TypeTable.StringObjectType);
			EPointer spv = new EPointer(sptr, sloc.Address);
			this.EvalStackPush(spv);
		}

		////////
		//Support for calls
		/// <summary>
		/// Assert that an argument parameter var is of a specific type.
		/// </summary>
		internal void SetArgType(IRType tt, int argpos)
		{
			Contract.Requires(tt != null);

			this.m_evalstack[argpos] = this.m_evalstack[argpos].RetypeAtCall(tt);
		}

		/// <summary>
		/// Take a reference to a freshly allocated object on top of the stack and rotate it into the correct position to be 
		/// the reciver argument on stack.
		/// </summary>
		internal void RotateBlankAllocToRecvrPosition(int rcvrpos)
		{
			EvalStackEntry rcn = this.EvalStackPopWVal();
			this.m_evalstack.Insert(rcvrpos, rcn);
		}

		[Pure]
		internal int GetFirstLocalStackPosForCall(int argc)
		{
			Contract.Requires(argc >= 0);

			return this.m_evalstack.Count - argc;
		}


		/// <summary>
		/// Return the resolved method target for a virtual call.
		/// </summary>
		internal IRType GetTargetCallClassForName(int rcvrpos, List<ELocation> memory, IRVirtualInvoke vinv)
		{
			Contract.Requires(vinv != null);

			EPointer pn = (EPointer)this.m_evalstack[rcvrpos].PointerValue;

			ENumeric nv = pn.PointerNullTest();
			if(nv.IsValueOne)
			{
				DebugReport.ReportError("Null reference on invoke");
				throw new InvalidOperationException();
			}

			EObject obj = ResolvePointerTarget(pn, memory);
			return obj.LocationType;
		}
	}
}
